Minimalistic SLIP model

last edits:

  • March 22, 2013 Moritz Maus (initial start)
  • April 3rd, 2013 Moritz Maus (transfer to server)
  • April 7th, 2013 Moritz Maus (enhanced KinData class)
  • April 8th, 2013 Moritz Maus (started "real scientific" work)
  • April 11th, 2013 Moritz Maus (started "bugfixing" in prediction)
  • April 15th, 2013 Moritz Maus (finalized bugfixig in prediction, added Multi-Section maps )
  • April 18th, 2013 Moritz Maus (started work on "factor" controlled SLIP)
  • April 18th, 2013 Moritz Maus (continued work on "factor" controlled SLIP)

prerequisites:

Requires IPython to run in "pylab" mode. If this is not the case, at least insert
from pylab import *
somewhere

synopsis:

This script tries to identify a minimalistic SLIP extension (e.g., Ankle-Y-position only).
Requirements:

  • System must be self-contained
  • System must have eigenvalues similar to original data
  • System must be approximately as predictable as "full ankle-SLIP"

Step 1: import data


In [105]:
# define and import data
subject = 2  #available subjects: 1,2,3,7. Other subjects have comparatively bad data quality
ttype = 1 # 1: free running, 2: metronome running (data incomplete!)
n_factors = 5 # 1-5 factors to predict SLIP parameters
exclude_IC_from_factors = False # exclude the initial conditions from kinematic state 
                               #prior to identifying SLIP parameter predictors?

In [106]:
import mutils.io as mio
import mutils.misc as mi
import os
import re

# load kinematic data
k = mio.KinData()
k.load(subject, ttype)
k.selection = ['r_anl_y - com_y', 'l_anl_y - com_y', 'com_z']

SlipData = [mi.Struct(mio.mload('../data/2013-newSlip/SLIP_s%it%ir%i.dict' % (subject, ttype, rep)))
           for rep in k.reps]

indices_right = [mi.upper_phases(d.phases[:-1], sep=0, return_indices=True) for d in SlipData]
indices_left = [mi.upper_phases(d.phases[:-1], sep=pi, return_indices=True) for d in SlipData]
starts_right = [idxr[0] < idxl[0] for idxr, idxl in zip(indices_right, indices_left)]
param_right = [ vstack(d.P)[idxr, :] for d, idxr in zip(SlipData, indices_right)]
param_left = [ vstack(d.P)[idxl, :] for d, idxl in zip(SlipData, indices_left)]
IC_right = [ vstack(d.IC)[idxr, :] for d, idxr in zip(SlipData, indices_right)]
IC_left = [ vstack(d.IC)[idxl, :] for d, idxl in zip(SlipData, indices_left)]

Step 2: test consistence (run SLIP)


In [107]:
#select any data
import models.sliputil as su
dat = SlipData[2]
stepnr = 32

print "simulation:",  su.finalState(dat.IC[stepnr], dat.P[stepnr], {'m' : dat.mass, 'g': dat.g})
print '==================='
print "experiment:", dat.IC[stepnr + 1]


simulation: [ 1.0155  2.8197 -0.0114]
===================
experiment: [ 1.0155  2.8197 -0.0114]

Step 3: find minimalistic SLIP predictors


In [108]:
# find minimal predictors
import mutils.statistics as st
import mutils.FDatAn as fda

# set apex data
# *NOTE* the first three labels have to be "com_x", "com_y", "com_z". If you edit this, make sure to edit the cell where data
# are further processed...
k.selection = [ 'com_x', 'com_y', 'com_z',
             'r_anl_y - com_y', 'r_anl_x - com_x', 'r_mtv_z - r_anl_z', 'r_mtv_x - r_anl_x', 'r_kne_y - com_y',
             'r_elb_y - com_y', 'r_elb_x - com_x', 'r_wrl_z - com_z', 'r_wrl_x - com_x', 'cvii_y - com_y',
             'l_anl_y - com_y', 'l_anl_x - com_x', 'l_mtv_z - l_anl_z', 'l_mtv_x - l_anl_x', 'l_kne_y - com_y',
             'l_elb_y - com_y', 'l_elb_x - com_x', 'l_wrl_z - com_z', 'l_wrl_x - com_x', ]

# sep = 0 -> right leg will touchdown next
# sep = pi -> right leg will touchdown next

# always exclude last apex - there are no SLIP parameters defined for it!
kin_right = k.get_kin_apex( [mi.upper_phases(d.phases[:-1], sep=0) for d in SlipData],)
kin_left  = k.get_kin_apex( [mi.upper_phases(d.phases[:-1], sep=pi) for d in SlipData],)

Definition: each stride starts with a right step


In [109]:
# build common dataset
all_kin_r = []
all_kin_l = []

all_param_r = []
all_param_l = []

all_IC_r = []
all_IC_l = []
for rep in arange(len(starts_right)): #kr, kl, sr in zip(kin_right, kin_left, starts_right):
    # when repetition starts with right step: select 
    kin_r = vstack(kin_right[rep]).T
    kin_l = vstack(kin_left[rep]).T
    par_r = param_right[rep]
    par_l = param_left[rep]
    IC_r = IC_right[rep]
    IC_l = IC_left[rep]
    if not starts_right[rep]:
        # omit first value in kin_l!
        kin_l = kin_l[1:, :]
        par_l = par_l[1:, :]
        IC_l = IC_l[1:, :]
    
    minlen = min(kin_r.shape[0], kin_l.shape[0])
    kin_r = hstack([kin_r[:minlen, 2 : len(k.selection) + 2] ,
                    kin_r[:minlen, len(k.selection) + 3 :]])# remove absolute position + vertical velocity
    kin_l = hstack([kin_l[:minlen, 2 : len(k.selection) + 2] ,
                    kin_l[:minlen, len(k.selection) + 3 :]])# remove absolute position + vertical velocity
    par_r = par_r[:minlen, :]
    par_l = par_l[:minlen, :]
    IC_r = IC_r[:minlen, :]
    IC_l = IC_l[:minlen, :]
    
    all_IC_r.append(IC_r)
    all_IC_l.append(IC_l)
    all_param_r.append(par_r)
    all_param_l.append(par_l)
    all_kin_r.append(kin_r)
    all_kin_l.append(kin_l)

all_IC_r = vstack(all_IC_r)
all_IC_l = vstack(all_IC_l)
all_param_r = vstack(all_param_r)
all_param_l = vstack(all_param_l)
all_kin_r = vstack(all_kin_r)
all_kin_l = vstack(all_kin_l)

In [109]:

Re-test consistency of SLIP data

NOTE Here, the mass is not exactly the same as in the parameter calculations. Thus, minor deviations may occur.


In [110]:
stepnr = 1299
mass = mean([d.mass for d in SlipData])
# su.finalState actually performs a one-step integration of SLIP
print "simres  [right step]:", su.finalState(all_IC_r[stepnr, :], all_param_r[stepnr, :], {'m' : mass, 'g' : -9.81})
print "subsequent left apex:", all_IC_l[stepnr, :]
print "==========="
print "simres    [left step]:", su.finalState(all_IC_l[stepnr, :], all_param_l[stepnr, :], {'m' : mass, 'g' : -9.81})
print "subsequent right apex:", all_IC_r[stepnr + 1, :]


simres  [right step]: [ 1.0044  2.9339 -0.0473]
subsequent left apex: [ 1.0048  2.9327 -0.0473]
===========
simres    [left step]: [ 1.0045  2.9425  0.0657]
subsequent right apex: [ 1.0048  2.9413  0.0657]

Compute predictors

first: prepare data


In [111]:
# detrend - look at residuals!
dt_kin_r = fda.dt_movingavg(all_kin_r, 30)
dt_kin_l = fda.dt_movingavg(all_kin_l, 30)
dt_param_r = fda.dt_movingavg(all_param_r, 30)
dt_param_l = fda.dt_movingavg(all_param_l, 30)
dt_IC_r = fda.dt_movingavg(all_IC_r, 30)
dt_IC_l = fda.dt_movingavg(all_IC_l, 30)

# use the *same* values for normalization for left and right!
# yes, it's 'sdt', not 'std': "Scores of DeTrented" ...
sdt_kin_r = dt_kin_r / std(dt_kin_r, axis=0)
sdt_kin_l = dt_kin_l / std(dt_kin_r, axis=0)
sdt_param_r = dt_param_r / std(dt_param_r, axis=0)
sdt_param_l = dt_param_l / std(dt_param_r, axis=0)

sdt_kin_r -= mean(sdt_kin_r, axis=0)
sdt_kin_l -= mean(sdt_kin_l, axis=0)
sdt_param_r -= mean(sdt_param_r, axis=0)
sdt_param_l -= mean(sdt_param_l, axis=0)

sdt_kin_r_noIC = hstack([sdt_kin_r[:,1:20],sdt_kin_r[:, 22:]])
sdt_kin_l_noIC = hstack([sdt_kin_l[:,1:20],sdt_kin_l[:, 22:]])

In [112]:
print dt_kin_r.shape, dt_param_r.shape
print dt_kin_l.shape, dt_param_l.shape


(1957, 41) (1957, 5)
(1957, 41) (1957, 5)

Select factors and dimensions of reduced model(s)


In [113]:
# parameter to predict. select [0,1,2,4] for 2D-SLIP (excluding beta), or [0,1,2,3,4] for full 3D SLIP
p2D_r = sdt_param_r[:, [0,1,2,3,4]]
p2D_l = sdt_param_l[:, [0,1,2,3,4]]

In [114]:
# Here, the main predictors ("directions") are computed. This is computationally quite fast
if exclude_IC_from_factors:
    facs_r = st.find_factors(sdt_kin_r_noIC.T, p2D_r.T, k=n_factors)
    facs_l = st.find_factors(sdt_kin_l_noIC.T, p2D_l.T, k=n_factors)

    fscore_r = dot(facs_r.T, sdt_kin_r_noIC.T).T
    fscore_l = dot(facs_l.T, sdt_kin_l_noIC.T).T

else:
    facs_r = st.find_factors(sdt_kin_r.T, p2D_r.T, k=n_factors)
    facs_l = st.find_factors(sdt_kin_l.T, p2D_l.T, k=n_factors)
    
    # project data on lower dimensional subspace (do not take to full space again)
    fscore_r = dot(facs_r.T, sdt_kin_r.T).T
    fscore_l = dot(facs_l.T, sdt_kin_l.T).T

Select one of these reduced models


In [115]:
# reduced model: 6D state, 3 predictors
#reddat_r = hstack([fscore_r, dt_IC_r])
#reddat_l = hstack([fscore_l, dt_IC_l])

# form a model that *only* conists of parts of the "full" data (*excatly* the same data),
# which however may be projected to some axes
IC_r_from_dt = sdt_kin_r[:,[0, 20, 21]]   # CoM height and horizontal plane velocities
IC_l_from_dt  = sdt_kin_l[:,[0, 20, 21]]
reddat_r = hstack([fscore_r, IC_r_from_dt])
reddat_l = hstack([fscore_l, IC_l_from_dt])

# reddat_r = fscore_r
# reddat_l = fscore_l

test predictive capabilities for SLIP parameter


In [116]:
# "select the initial conditions from full state information" - matrix
# this matrix only makes sense if exclude_IC_from_factors is set to False!
if not exclude_IC_from_factors:
    IC_sel = zeros((41, 3))
    IC_sel[0, 0] = 1
    IC_sel[20, 1] = 1
    IC_sel[21, 2] = 1
    
    rm_selector_r = hstack([facs_r, IC_sel])
    rm_selector_l = hstack([facs_l, IC_sel])

test: sanity check of code


In [117]:
# manually perform prediction. Here: in-sample!
reload(st)
idat1 = sdt_kin_r
if not exclude_IC_from_factors:
    idat2 = dot(rm_selector_r.T, sdt_kin_r.T).T
else:
    idat2 = reddat_r
odat = p2D_r
print "Data is the same as 'original' reduced model:", allclose(idat2, reddat_r)

# perform regression:
vred1 = []
vred2 = []
vred1c = []
vred2c = []
for rep in range(50):
    sel_idx = randint(0, odat.shape[0], odat.shape[0])
    # test: remove double indices (this is done internally in vRedPartial)
    # -> then, vRedPartial will return identical results
    sel_idx = array(list(set(sel_idx)))
    A1 = lstsq(idat1[sel_idx, :], odat[sel_idx, :])[0]
    A2 = lstsq(idat2[sel_idx, :], odat[sel_idx, :])[0]
    # test
    #A1b = dot(pinv(idat1[sel_idx, :]), odat[sel_idx, :])
    #A2b = dot(pinv(idat1[sel_idx, :]), odat[sel_idx, :])
    #print "computation as expected:", allclose(A1, A1b), allclose(A1, A1b)
    # compute predictions
    pred1 = dot(idat1[sel_idx, :], A1)
    pred2 = dot(idat2[sel_idx, :], A2)
    # compute relative remaining variance after prediction
    rvar1 = var(odat[sel_idx, :] - pred1, axis=0) / var(odat[sel_idx, :], axis=0)
    rvar2 = var(odat[sel_idx, :] - pred2, axis=0) / var(odat[sel_idx, :], axis=0)
    rvar1c = fda.vRedPartial(idat1, odat, [A1, ], [fda.otheridx(sel_idx,  odat.shape[0]), ], vaxis=0)[0]
    rvar2c = fda.vRedPartial(idat2, odat, [A2, ], [fda.otheridx(sel_idx, odat.shape[0]), ], vaxis=0)[0]
    # store results
    vred1.append(rvar1)
    vred2.append(rvar2)
    vred1c.append(rvar1c)
    vred2c.append(rvar2c)
    

vred1b = st.predTest(idat1, odat, out_of_sample=False, rcond=1e-7)    
vred2b = st.predTest(idat2, odat, out_of_sample=False, rcond=1e-7)


Data is the same as 'original' reduced model: True

In [118]:
#visualize
figure()
b1 = boxplot(vstack(vred1), positions = arange(odat.shape[1]) + 1, widths=.15)
b2 = boxplot(vstack(vred2), positions = arange(odat.shape[1]) + 1.1, widths=.15)

#b1b = boxplot(vstack(vred1c), positions = arange(odat.shape[1]) + 1.3, widths=.15)
#b2b = boxplot(vstack(vred2c), positions = arange(odat.shape[1]) + 1.4, widths=.15)


b1b = boxplot(vstack(vred1b), positions = arange(odat.shape[1]) + 1.3)
b2b = boxplot(vstack(vred2b), positions = arange(odat.shape[1]) + 1.4)


mi.recolor(b1, 'k')
mi.recolor(b2, 'r')

mi.recolor(b1b, 'b')
mi.recolor(b2b, 'm')

xlim(0, 6)
ylim(.4,.45)
draw()


end sanity check

Test predictive capabilities for SLIP parameters of full and reduced models

Note: to make a quick sanity check, you can change the "out-of-sample" prediction parameter to "False" to get in-sample prediction


In [119]:
predict_oos = True
# select reduced models as a real subset of the full kinematic state at apex
if not exclude_IC_from_factors:
    # use a real projection of the data!
    reddat_r = dot(rm_selector_r.T, sdt_kin_r.T).T
    reddat_l = dot(rm_selector_l.T, sdt_kin_l.T).T

#p2D_r = p2D_r - mean(p2D_r, axis=0)
#sdt_kin_r = sdt_kin_r - mean(sdt_kin_r, axis=0)

res1 = st.predTest(reddat_r, p2D_r, out_of_sample=predict_oos, rcond=1e-7)
res2 = st.predTest(sdt_kin_r, p2D_r, out_of_sample=predict_oos, rcond=1e-7)

res3 = st.predTest(reddat_l, p2D_l,  out_of_sample=predict_oos, rcond=1e-7)
res4 = st.predTest(sdt_kin_l, p2D_l, out_of_sample=predict_oos, rcond=1e-7)

figure(figsize=(12,6))
subplot(1,2,1)
b1 = boxplot(res1)
b2 = boxplot(res2)
mi.recolor(b1, 'r')
mi.recolor(b2, 'k')
xticks(arange(5) + 1)
gca().set_xticklabels(['k', r'$\alpha$', r'L$_0$', r'$\beta$', r'$\delta$E'], fontsize=16)
xlabel('predicted variable')
ylabel('relative remaining variance')
ylim(0, 1.05)
title('right step SLIP params\nred: reduced model,\nblack: full kinematic state')
subplot(1,2,2)
b1 = boxplot(res3)
b2 = boxplot(res4)
mi.recolor(b1, 'r')
mi.recolor(b2, 'k')
xticks(arange(5) + 1)
gca().set_xticklabels(['k', r'$\alpha$', r'L$_0$', r'$\beta$', r'$\delta$E'], fontsize=16)
xlabel('predicted variable')
ylabel('relative remaining variance')
ylim(0, 1.05)
title('left step SLIP params\nred: reduced model,\nblack: full kinematic state')

draw()


Test self-predictability of the model

Note: to make a quick sanity check, you can change the "out-of-sample" prediction parameter to "False" to get in-sample prediction


In [120]:
# test self-consistency

oos_pred = True # out of sample prediction?

# predict
res1 = st.predTest(reddat_r, reddat_l, out_of_sample=oos_pred, rcond=1e-7)
res2 = st.predTest(sdt_kin_r, reddat_l, out_of_sample=oos_pred, rcond=1e-7)

res3 = st.predTest(reddat_l[:-1, :], reddat_r[1:, :], out_of_sample=oos_pred, rcond=1e-7)
res4 = st.predTest(sdt_kin_l[:-1, :], reddat_r[1:, :], out_of_sample=oos_pred, rcond=1e-7)


figure(figsize=(16,8))
subplot(1,2,1)
b1 = boxplot(res1)
b2 = boxplot(res2)
mi.recolor(b1, 'r')
mi.recolor(b2, 'k')
ylim(0, 1.05)
xticks(arange(res3.shape[1]) + 1)
gca().set_xticklabels(['factor ' + str(nr + 1) for nr in arange(res1.shape[1] - 3)] +
      ['height', 'horiz. speed', 'lat. speed'], rotation=45)
title('predicting left apex state\nred: reduced model, black: full model')
ylabel('relative remaining variance')

subplot(1,2,2)
b1 = boxplot(res3)
b2 = boxplot(res4)
mi.recolor(b1, 'r')
mi.recolor(b2, 'k')
ylim(0, 1.05)
xticks(arange(res3.shape[1]) + 1)
gca().set_xticklabels(['factor ' + str(nr + 1) for nr in arange(res1.shape[1] - 3)] +
      ['height', 'horiz. speed', 'lat. speed'], rotation=45)
title('predicting left apex state\nred: reduced model, black: full model')
ylabel('relative remaining variance')

draw()


eigenvalue analysis


In [121]:
# eigenvalue analyis

# full model
_, maps_1, _ = fda.fitData(sdt_kin_r, sdt_kin_l, nps=1, nrep=200, rcond=1e-7)
_, maps_2, _ = fda.fitData(sdt_kin_l[:-1, :], sdt_kin_r[1:, :], nps=1, nrep=200, rcond=1e-7, )
maps_full = [dot(m1, m2) for m1, m2 in zip(maps_1, maps_2)]

# reduced model
_, maps_1, _ = fda.fitData(reddat_r, reddat_l, nps=1, nrep=200, rcond=1e-7)
_, maps_2, _ = fda.fitData(reddat_l[:-1, :], reddat_r[1:, :], nps=1, nrep=200, rcond=1e-7, )
maps_red = [dot(m1, m2) for m1, m2 in zip(maps_1, maps_2)]

In [122]:
import fmalibs.util as ut

vi_full = ut.VisEig(127, False)
for A in maps_full:
    vi_full.add(eig(A)[0])

vi_red = ut.VisEig(127, False)
for A in maps_red:
    vi_red.add(eig(A)[0])
    
    
figure(figsize=(18, 9))
subplot(1,2,1)
vi_full.vis1()
xlim(-1,1)
ylim(-1,1)
title('eigenvalue distribution "full model"')
subplot(1,2,2)
vi_red.vis1()
vi_full.vis1()
xlim(-1,1)
ylim(-1,1)
title('eigenvalue distribution "reduced model"')


Out[122]:
<matplotlib.text.Text at 0x797e590>

Enhance number of sections per stride


In [123]:
nps = 30 # number of sections per stride
map_sections = [0, 8, 15, 23] # select some sections for which mappings should be computed
map_sections = [0, 5, 10, 15, 20, 25] # select some sections for which mappings should be computed
#map_sections = [0, 7, 13, 19,25] # select some sections for which mappings should be computed
#map_sections = [0, 10, 20] # select some sections for which mappings should be computed
k_n = fda.dt_movingavg(k.make_1D(nps=nps)[:, 2 * nps:], 30) # exclude horizontal CoM position
k_n -= mean(k_n, axis=0)
k_n /= std(k_n, axis=0)
maps_pt, maps_full, idcs = fda.fitData(k_n[:-1, :], k_n[1:, :], nps=nps, sections=map_sections, nrep=200, rcond=1e-7 )

In [124]:
# compute stride maps from sections maps
# note: the correct order in the product of maps is  map_n * map_(n-1) * ... * map_1
# otherwise, the resulting matrix is not a propagator
# -> reverse ordering of maps
all_maps = [reduce(dot, maps[::-1]) for maps in zip(*maps_pt)]

vi3 = ut.VisEig(127, False)
for A in all_maps:
    vi3.add(eig(A)[0])

figure(figsize=(18, 6))

subplot(1,3,1)
vr = vi_full.vis1()[0]
vr.set_cmap(cm.jet)
xlim(-1,1)
ylim(-1,1)
title('eigenvalue distribution "full model"')

subplot(1,3,2)

vr = vi_full.vis1()[0]
vr.set_cmap(cm.jet)
vr = vi_red.vis1()[0]
vr.set_cmap(cm.hot)
xlim(-1,1)
ylim(-1,1)
title('eigenvalue distribution "reduced model" (red)\n vs. full model (sections at apex)')


subplot(1,3,3)
vr = vi3.vis1()[0]
vr.set_cmap(cm.jet)
vr = vi_red.vis1()[0]
vr.set_cmap(cm.hot)
xlim(-1,1)
ylim(-1,1)
title('eigenvalues of full kinematic model\nwith %i sections per stride\n vs. reduced model(red)'
  % len(map_sections))
xlabel('real part')
ylabel('imaginary part')

draw()


TODO: adapt SLIP model with controller and compare eigenvalues with "direct" computed eigenvalues.

Hypothesis: There should be almost no difference according to results from my thesis.
The closed loop model reads as follows:

Step 1 ("right"):
state = [IC; Factors] where IC = initial CoM state at apex
params = P1 * state where P is a regression from data
new_state = [ode(IC, params); Q1 * [IC; Factors]] where Q is a regressed map from data

Step 2 ("left"):
state = [IC; Factors] where IC = initial CoM state at apex params = P2 * state where P is a regression from data new_state = [ode(IC, params); Q2 * [IC; Factors]] where Q is a regressed map from data

stage 1: find periodic solution

Approach: find periodic solution that corresponds to the average parameters, and verify that it's close to the average initial conditions


In [125]:
mean_pr = mean(vstack(param_right), axis=0)
mean_pl = mean(vstack(param_left), axis=0)
mean_ICr = mean(all_IC_r, axis=0)
mean_ICl = mean(all_IC_l, axis=0)
mean_pl[4] = -mean_pr[4] # set total energy change to exactly zero. 
# Note: typically, the difference is low, roughly ~0.01 J 
ICp_r, ICp_l = su.getPeriodicOrbit_p(mean_pr, mean_pl, aug_dict={'m': mass, 'g' : -9.81}, startState=mean_ICr)

In [126]:
#verify periodicity
sr1 = su.finalState(ICp_r, mean_pr, addDict = {'m' : mass, 'g' : -9.81})
sl1 = su.finalState(ICp_l, mean_pl, addDict = {'m' : mass, 'g' : -9.81})
print "difference between identified periodic orbit and simulation results:"
print sr1 - ICp_l
print sl1 - ICp_r


difference between identified periodic orbit and simulation results:
[ -3.4797e-07   2.9524e-07   2.6958e-07]
[ 0.  0.  0.]

In [127]:
print "Deviation from periodic orbit to average apex state, in units of standard deviations:"
print "           [height, horizontal speed, lateral speed]"
print "left step: ", (ICp_l - mean_ICl) / std(all_IC_l, axis=0)
print "right step:", (ICp_r - mean_ICr) / std(all_IC_r, axis=0)


Deviation from periodic orbit to average apex state, in units of standard deviations:
           [height, horizontal speed, lateral speed]
left step:  [-0.1718  0.1255  0.0267]
right step: [-0.1649  0.1183 -0.0115]

stage 2: compute parameter prediction maps ("controller") and "factors" prediction


In [128]:
print dt_kin_r.shape, dt_param_r.shape
print dt_kin_l.shape, dt_param_l.shape


(1957, 41) (1957, 5)
(1957, 41) (1957, 5)

In [128]:


In [129]:
dt_fstate_r = hstack([fda.dt_movingavg(all_IC_r, 30), fscore_r])
dt_fstate_l = hstack([fda.dt_movingavg(all_IC_l, 30), fscore_l])
dt_fstate_r -= mean(dt_fstate_r, axis=0)
dt_fstate_l -= mean(dt_fstate_l, axis=0)


#dt_param_r = fda.dt_movingavg(vstack(param_right), 30)
#dt_param_l = fda.dt_movingavg(vstack(param_left), 30)
#dt_param_r -= mean(dt_param_r, axis=0)
#dt_param_l -= mean(dt_param_l, axis=0)


# compute parameter prediction maps
_, all_ctrl_r, _ = fda.fitData(dt_fstate_r, dt_param_r, nps=1, nrep=200, sections=[0,], rcond=1e-8)
_, all_ctrl_l, _ = fda.fitData(dt_fstate_l, dt_param_l, nps=1, nrep=200, sections=[0,], rcond=1e-8)
ctrl_r = fda.meanMat(all_ctrl_r)
ctrl_l = fda.meanMat(all_ctrl_l)

# compute factors prediction maps - "propagators" of factor's state
_, all_facprop_r, _ = fda.fitData(dt_fstate_r, fscore_l, nps=1, nrep=200, sections=[0,], rcond=1e-8)
_, all_facprop_l, _ = fda.fitData(dt_fstate_l[:-1, :], fscore_r[1:, :], nps=1, nrep=200, sections=[0,], rcond=1e-8)
facprop_r = fda.meanMat(all_facprop_r)
facprop_l = fda.meanMat(all_facprop_l)

In [130]:
std(array(all_facprop_l), axis=0)
figure()
subplot(1,2,1), pcolor(mean(array(all_facprop_l), axis=0)), colorbar()
subplot(1,2,2), pcolor(std(array(all_facprop_l), axis=0)), colorbar()


Out[130]:
(<matplotlib.axes.AxesSubplot at 0xc9a6990>,
 <matplotlib.collections.PolyCollection at 0xc62bad0>,
 <matplotlib.colorbar.Colorbar instance at 0x96bcef0>)

In [131]:
print mean(st.predTest(dt_fstate_l[:-1,:], fscore_r[1:, :], totvar=False, out_of_sample=True), axis=0)
print mean(st.predTest(dt_fstate_r, fscore_l, totvar=False), axis=0)
figure(figsize=(16,4)), subplot(1,2,1), pcolor(facprop_r), colorbar()
subplot(1,2,2), pcolor(facprop_l), colorbar()


[ 0.7277  0.3674  0.7405  0.6868  0.559 ]
[ 0.6751  0.4588  0.7198  0.6411  0.6569]
Out[131]:
(<matplotlib.axes.AxesSubplot at 0xc647810>,
 <matplotlib.collections.PolyCollection at 0x8b98690>,
 <matplotlib.colorbar.Colorbar instance at 0x8b97b90>)

stage 3a: define stride function of controlled SLIP

Note that this function does not depend on the actual shape of the factors, i.e. if there are 3 or 5 factors.


In [132]:
def controlled_stride(fullstate, param0_r, param0_l, refstate_r, refstate_l, ctrl_r,
         ctrl_l, facprop_r, facprop_l, addDict):
    """
    This function performs a stride of the controlled SLIP. Control maps and reference
    motion / values must be given.

    :args:
        fullstate ([k+3]x float): the initial full state of the system: [CoM state; factors]
        param0_r (5x float): the reference leg parameters for right leg in
            the periodic motion: [k, alpha, L0, beta, dE]
        param0_l (5x float): the reference leg parameters for left leg
        refstate_r (3x float): the right apex state for periodic motions:
            [height, horiz. speed, lat. speed]. *NOTE* The reference value for factors is always zero.
        refstate_l (3x float): the left apex state for periodic motions
        ctrl_r (2D array): a map that predicts the off-reference values for SLIP parameters as linear function 
            of the full state (which is [CoM state; factors].T). The "right leg controller"
        ctrl_l (2D array): the corresponding "left leg controller"
        facprop_r (2D array): The propagator that maps the right full (apex) state to the factors at left apex.
        facprop_l (2D array): The propagator that maps the left full (apex) state to the factors at right apex.
        addDict (dict): contains 'm' (model mass in kg) and 'g', the gravity (typically -9.81 [ms^-2])
    :returns:
        final state ([k+3]x float)): the final full state of the system [CoM state; factors] after one stride.

    *NOTE* it is assumed that the reference values for the "factors" are zero.

    """
    # == right step ==
    IC = fullstate.squeeze()[:3]    
    d_state_r = fullstate.squeeze() - refstate_r.squeeze()
    # compute SLIP control input
    d_param_r = dot(ctrl_r, d_state_r)
    p_r = param0_r.squeeze() + d_param_r.squeeze()
    # simulate SLIP
    com_state_l = su.finalState(IC, p_r, addDict).squeeze()    
    # propagate factors state
    facs_state_l = dot(facprop_r, d_state_r).squeeze()
    fullstate_l = hstack([com_state_l, facs_state_l])
    
    # == left step ==
    IC_l = fullstate_l[:3]
    d_state_l = fullstate_l - refstate_l.squeeze()
    # DEBUG
    #print "d state l    :", d_state_l
    # compute SLIP control input
    d_param_l = dot(ctrl_l, d_state_l)
    p_l = param0_l.squeeze() + d_param_l.squeeze()
    # simulate SLIP
    # DEBUG
    #print "dstate r:", d_state_r
    #print "dstate l:", d_state_l
    #print "p: ", p_l
    #print "com: ", IC_l
    #return 2
    com_state_final = su.finalState(IC_l, p_l, addDict).squeeze()
    # propagate factors state
    facs_state_final = dot(facprop_l, d_state_l).squeeze()
    fullstate_final = hstack([com_state_final, facs_state_final])
    # DEBUG
    #print "d state final:", fullstate_final - refstate_r.squeeze()
    
    return fullstate_final


def get_auto_sys(param0_r, param0_l, refstate_r, refstate_l, ctrl_r, ctrl_l, facprop_r, facprop_l, addDict):
    """
    Returns a function f that expresses the autonomous system: x_(n+1) = f (x_n).
    This is a convenience function for e.g. calculating the jacobian.

    :args:
        param0_r (5x float): the reference leg parameters for right leg in
            the periodic motion: [k, alpha, L0, beta, dE]
        param0_l (5x float): the reference leg parameters for left leg
        refstate_r (3x float): the right apex state for periodic motions:
            [height, horiz. speed, lat. speed]. *NOTE* The reference value for factors is always zero.
        refstate_l (3x float): the left apex state for periodic motions
        ctrl_r (2D array): a map that predicts the off-reference values for SLIP parameters as linear function 
            of the full state (which is [CoM state; factors].T). The "right leg controller"
        ctrl_l (2D array): the corresponding "left leg controller"
        facprop_r (2D array): The propagator that maps the right full (apex) state to the factors at left apex.
        facprop_l (2D array): The propagator that maps the left full (apex) state to the factors at right apex.
        addDict (dict): contains 'm' (model mass in kg) and 'g', the gravity (typically -9.81 [ms^-2])
    :returns:
        a lambda function f(fullstate) that calls the controlled_stride function using the given parameters.        
    """
    return lambda fullstate: controlled_stride(fullstate, param0_r, param0_l, refstate_r, refstate_l, ctrl_r,
         ctrl_l, facprop_r, facprop_l, addDict)

stage 3b: create autonomous system


In [133]:
allow_nonzero_ref = 1 # set this to zero to force "zero" as reference for the fscores!
#mean_ICl
refstate_r = hstack([ICp_r, allow_nonzero_ref * mean(fscore_r, axis=0)]) # the latter should be zero anyway
refstate_l = hstack([ICp_l, allow_nonzero_ref * mean(fscore_l, axis=0)]) # the latter should be zero anyway
    
f = get_auto_sys(mean_pr, mean_pl,  refstate_r, refstate_l, ctrl_r, ctrl_l, facprop_r, facprop_l, {'m': mass, 'g':-9.81})
#IC = refstate_r
#stride_res =  f(refstate_r + array([.001, 0, 0, 0, 0, 0,0,0]))
#print stride_res
#print stride_res - refstate_r
#print "======="
#controlled_stride(refstate_r, mean_pr, mean_pl,  refstate_r, refstate_l, ctrl_r, ctrl_l, facprop_r, facprop_l, {'m': 50, 'g':-9.81})

In [134]:
print var((dt_param_l -  dot (ctrl_l, dt_fstate_l.T).T), axis=0) / var(dt_param_l, axis=0)
print var((dt_param_r -  dot (ctrl_r, dt_fstate_r.T).T), axis=0) / var(dt_param_r, axis=0)


[ 0.3937  0.1826  0.2227  0.0696  0.2833]
[ 0.3889  0.1481  0.2273  0.0847  0.3348]

In [135]:
# test sensitivity. This is bugfixing prior to computation of Jacobian.
h = .0001
elem = 0
IC = refstate_r.copy()
IC[elem] += h
fsp = f(IC)
IC[elem] -= 2*h
fsn = f(IC)
set_printoptions(precision=4)
print array(fsp - refstate_r) / (2.*h)
print array(fsn - refstate_r) / (2.*h)


[  0.2246  -0.3388  -2.0621 -11.4488 -49.2587  21.4914 -95.6396  29.644 ]
[ -0.2338   0.3529   2.0617  11.5919  49.3966 -22.5288  97.3508 -30.5082]

stage 4: compute jacobian


In [136]:
J = []
h = .001
for elem in range(len(refstate_r)):
    IC = refstate_r.copy()
    IC[elem] += h
    fsp = f(IC)
    IC[elem] -= 2*h
    fsn = f(IC)
    J.append((fsp - fsn) / (2.*h))
    
J = vstack(J).T

In [137]:
print eig(J)[0]
figure()
for ev in eig(J)[0]:
    plot(ev.real, ev.imag, 'rd')
    
#t = linspace(0, 2.*pi)
#plot(sin(t), cos(t), 'k--')
#plot(.5*sin(t), .5*cos(t), 'k--')
axis('equal')

vi_red.vis1()
xlim(-1,1)
ylim(-1,1)


[ 0.3445+0.0515j  0.3445-0.0515j -0.2038+0.1879j -0.2038-0.1879j
  0.0780+0.j      0.0252+0.j     -0.0305+0.0116j -0.0305-0.0116j]
Out[137]:
(-1, 1)

In [138]:
# compute stride maps from sections maps
# note: the correct order in the product of maps is  map_n * map_(n-1) * ... * map_1
# otherwise, the resulting matrix is not a propagator
# -> reverse ordering of maps
all_maps = [reduce(dot, maps[::-1]) for maps in zip(*maps_pt)]

vi3 = ut.VisEig(127, False)
for A in all_maps:
    vi3.add(eig(A)[0])

figure(figsize=(18, 6))

subplot(1,3,1)

vr = vi_full.vis1()[0]
vr.set_cmap(cm.jet)
vr = vi_red.vis1()[0]
vr.set_cmap(cm.hot)
xlim(-1,1)
ylim(-1,1)
title('eigenvalue distribution "reduced model" (red)\n vs. full model (sections at apex)')


subplot(1,3,2)
vr = vi3.vis1()[0]
vr.set_cmap(cm.jet)
vr = vi_red.vis1()[0]
vr.set_cmap(cm.hot)
xlim(-1,1)
ylim(-1,1)
title('eigenvalues of full kinematic model\nwith %i sections per stride\n vs. reduced model(red)'
  % len(map_sections))
xlabel('real part')
ylabel('imaginary part')



subplot(1,3,3)
vr = vi3.vis1()[0]
vr.set_cmap(cm.jet)
vr = vi_red.vis1()[0]
vr.set_cmap(cm.hot)
for ev in eig(J)[0]:
    plot(ev.real, ev.imag, 'kd')
    
xlim(-1,1)
ylim(-1,1)
title('\n'.join(['eigenvalues of full kinematic model',
'with %i sections per stride', 'vs. reduced model(red)',
      'vs. forward model (diamonds)'])
  % len(map_sections))
xlabel('real part')
ylabel('imaginary part')


draw()


TODO: "rotate" factors such that the same subspace is spanned by them, however minimizing the L1-norm

approach (idea):

  • build a parametrized rotation matrix
  • calculate the L1-norm as function of the parmeters
  • use gradient descent or some other optimization routine

In [139]:
mag_thresh = .33
#mag_thresh = 0
facs_l_large = array(abs(facs_l) > mag_thresh) * facs_l
facs_r_large = array(abs(facs_r) > mag_thresh) * facs_r

figure(figsize=(15,4))
subplot(2,1,1)
pcolor(facs_l_large.T)
colorbar()
clim(-1,1)
xlim(0,41)
gca().set_yticks(arange(3) + .5)
gca().set_yticklabels([])
gca().set_xticks(arange(41) + .5)
gca().set_xticklabels([])
ylabel('for left leg')
title('elements of factors for predicting SLIP parameters, thresholded')
subplot(2,1,2)
pcolor(facs_r_large.T)
colorbar()
lbls = k.selection[2:] + [('v_' + pt) for pt in k.selection[:2] + k.selection[3:]]
gca().set_xticks(arange(41) + .5)
gca().set_xticklabels(lbls, rotation=90)
gca().set_yticks(arange(3) + .5)
ylabel('for right leg')
gca().set_yticklabels([])
clim(-1,1)
xlim(0,41)

print len(lbls)


41

In [140]:
# test autonomy of system [IC; factor 2; factor 3]
# test autonomus systems: ankle-SLIP unilateral / bilateral 
# find good reduced EXPLICIT model!

# John: looking only at differences, it should be possible to compute the jacobian without knowing the fix point.

# maybe re-compute periodic orbit w.r.t. mean IC

# can we rotate the 5-factor state to COM-state + 2 factors?